home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
3_0
/
SPIRO_SO
/
SPIRO.C
< prev
next >
Wrap
Text File
|
1989-06-18
|
22KB
|
885 lines
/*
** SPIRO.C
** John M. Nalezny
**
** This is a spirograph simulation program. You get to select
** The sizes of the two gears, the place to put the pen. Due to excessive
** use of sine/cosine, the first version is expected to be a dog.
** The use of a sine cache should clear that up. Those of you with
** Math coprocessors probably won't know the difference, but those
** of us without them should be delighted. P.S. the sine cache is
** about 5 times faster than making the function call every time.
**
** This program uses a window as a non-modal dialog box to handle the
** settings, like gear size, color, and pause state. If I had a color
** system, I would also want to add color selections, and I might anyway.
** I just hate to try to debug programs that use hardware I don't have.
** This program also is multi-finder friendly. When it is drawing,
** it gives up about 30 percent of the time to background stuff. At
** other times, it gives up about 95 percent. Seems like a good idea
** with version 7.0 comming (everybody in multifinder)
** How do you like that About Box? Thanks to the Symantec people
** for that one.
**
** History:
** May 29 89 JMN
** Program born.
**
** June 3 89 JMN
** make sure It can't go outside the box, and add growIcon
**
** June 10 89 JMN
** Multifinder friendly, and refreshing screen properly.
**
** June 17 89 JMN
** Now it does a formally correct spirograph curve. Also, cured
** roundoff desease by using nominator and denominator stuff.
*/
/* include lots of stuff. */
#include <QuickDraw.h>
#include <MacTypes.h>
#include <FontMgr.h>
#include <WindowMgr.h>
#include <MenuMgr.h>
#include <TextEdit.h>
#include <DialogMgr.h>
#include <EventMgr.h>
#include <DeskMgr.h>
#include <FileMgr.h>
#include <ToolboxUtil.h>
#include <ControlMgr.h>
#include <math.h>
#include <storage.h>
#include <unix.h>
#include <PrintMgr.h>
#include "ftrig.h"
/* I probably include too much stuff, but I find it easier to include
** too much and just not worry about it
*/
#define NIL 0L
#define VISIBLE 1
#define APPLE_MENU 129
#define FILE_MENU 130
#define EDIT_MENU 131
#define PRINT_M 1
#define QUIT_M 2
#define CUT_M 2
#define COPY_M 3
#define PASTE_M 4
#define CLEAR_M 5
#define DIALOG_ID 129
#define ABOUT_BOX_ID 128
#define STOP 1
#define GO 2
#define PEN_POS_MAX 20
#define WINDOW_ID 128
int errno;
WindowPtr draw_window, dlg_window;
WindowRecord draw_window_record, dlg_window_record;
int quit_whole_program = 0; /* global flag */
MenuHandle apple_menu_h;
MenuHandle file_menu_h;
MenuHandle edit_menu_h;
ControlHandle pause_control;
ControlHandle gear_1_size_control;
ControlHandle gear_speed_control;
ControlHandle gear_2_size_control;
ControlHandle pen_position_control;
long gear_1_speed_num, gear_1_speed_denom;
int point_number;
int gear_1_size = 36;
int gear_2_size = 40;
int gear_2_speed = 10;
int pen_position = PEN_POS_MAX;
int scale_nom, scale_denom;
int pause_state = STOP;
int last_x, last_y;
int in_foreground = 1; /* are we in the foreground? */
int wne_implemented; /* is wait next_event implemented? */
double ftrig(int, int);
PicHandle spiro_pic = NIL;
void init_managers()
{
int i;
MaxApplZone();
for ( i = 0 ; i < 10 ; i++ )
MoreMasters();
/* Seems like a good practice to do this */
InitGraf(&thePort);
InitFonts();
InitWindows();
FlushEvents(everyEvent,0);
InitWindows();
InitCursor();
InitMenus(); /* init menu bar stuff */
TEInit();
InitDialogs(NIL); /* I don't want to handle a system reset yet */
}
void setup_fake_dialog()
{
Handle w_dim; /* window dimentions resource */
long res_type, dummy;
int *ptr;
dlg_window = GetNewWindow( DIALOG_ID, &dlg_window_record, -1L);
/* get resource window */
sprintf(&res_type, "WDIM");
w_dim = GetResource(res_type, DIALOG_ID);
HLock(w_dim);
ptr = (int*)(*w_dim);
MoveWindow(dlg_window, *(ptr + 1), *ptr, TRUE);
SizeWindow(dlg_window, *(ptr + 3) - *(ptr + 1),
*(ptr + 2) - *ptr, TRUE);
HUnlock(w_dim);
ShowWindow(dlg_window);
SetPort(dlg_window);
}
void setup_draw_window()
{
Handle w_dim; /* window dimentions resource */
long res_type, dummy;
int *ptr;
draw_window = GetNewWindow( WINDOW_ID, &draw_window_record, -1L);
/* get resource window */
sprintf(&res_type, "WDIM");
w_dim = GetResource(res_type, WINDOW_ID);
HLock(w_dim);
ptr = (int*)(*w_dim);
MoveWindow(draw_window, *(ptr + 1), *ptr, TRUE);
SizeWindow(draw_window, *(ptr + 3) - *(ptr + 1),
*(ptr + 2) - *ptr, TRUE);
HUnlock(w_dim);
ShowWindow(draw_window);
pause_state = STOP;
SetPort(dlg_window);
update_fake_dialog();
calculate_scale();
}
remember_window_positions()
{
Handle w_dim; /* window dimentions resource */
long res_type, dummy;
int *ptr;
Point pt;
sprintf(&res_type, "WDIM");
w_dim = GetResource(res_type, DIALOG_ID); /* start with dialog window*/
HLock(w_dim);
ptr = (int*)(*w_dim);
SetPort(dlg_window);
pt.h = pt.v = 0; /* upper left hand corner */
LocalToGlobal(&pt);
*ptr = pt.v;
*(ptr + 1) = pt.h;
pt.h = dlg_window->portRect.right;
pt.v = dlg_window->portRect.bottom;
LocalToGlobal(&pt);
*(ptr + 2) = pt.v;
*(ptr + 3) = pt.h;
ChangedResource(w_dim);
WriteResource(w_dim); /* done with dialog window */
HUnlock(w_dim);
w_dim = GetResource(res_type, WINDOW_ID); /* start with draw window*/
HLock(w_dim);
ptr = (int*)(*w_dim);
SetPort(draw_window);
pt.h = pt.v = 0; /* upper left hand corner */
LocalToGlobal(&pt);
*ptr = pt.v;
*(ptr + 1) = pt.h;
pt.h = draw_window->portRect.right;
pt.v = draw_window->portRect.bottom;
LocalToGlobal(&pt);
*(ptr + 2) = pt.v;
*(ptr + 3) = pt.h;
ChangedResource(w_dim);
WriteResource(w_dim); /* done with draw window */
HUnlock(w_dim);
}
update_fake_dialog()
{
Rect box;
char txt[256];
KillControls(dlg_window); /* clear the decks of old controls */
EraseRect(&dlg_window->portRect);
SetRect(&box, 5,5,120,25);
if (pause_state == GO)
sprintf(txt, "\pSTOP");
else
sprintf(txt, "\pGO");
pause_control = NewControl(dlg_window, &box, txt, VISIBLE,0,0,0,
pushButProc, 0L);
MoveTo(5,45);
DrawString("\pStationary Gear Size");
SetRect(&box, 5,49,120, 65);
gear_1_size_control = NewControl(dlg_window, &box, "\pHello", VISIBLE,
gear_1_size,20,100, scrollBarProc, 0L);
MoveTo(5,85);
DrawString("\pMoving Gear Size");
SetRect(&box, 5, 89,120,105);
gear_2_size_control = NewControl(dlg_window, &box, "\pHello", VISIBLE,
gear_2_size,20,100, scrollBarProc, 0L);
MoveTo(5,125);
DrawString("\pSpeed");
SetRect(&box, 5,129,120, 145); /* top left bottom right */
gear_speed_control = NewControl(dlg_window, &box, "\pHello", VISIBLE,
gear_2_speed,0,25, scrollBarProc, 0L);
MoveTo(5,165);
DrawString("\pPen Position");
SetRect(&box, 5,169,120, 185); /* top left bottom right */
pen_position_control = NewControl(dlg_window, &box, "\pHello", VISIBLE,
pen_position,0,PEN_POS_MAX, scrollBarProc, 0L);
calculate_gear_speeds();
}
void setup_menus()
{
char apple[256], txt[256];
sprintf(apple,"\p%c", appleMark);
apple_menu_h = NewMenu(APPLE_MENU, apple);
AppendMenu(apple_menu_h, "\pAbout Spiro;(-");
AddResMenu(apple_menu_h, 'DRVR');
InsertMenu(apple_menu_h, 0); /* this should be the applemenu */
sprintf(txt,"\pFile");
file_menu_h = NewMenu(FILE_MENU, txt);
AppendMenu(file_menu_h, "\pPrint/P;Quit/Q");
InsertMenu( file_menu_h, 1);
sprintf(txt,"\pEdit");
edit_menu_h = NewMenu(EDIT_MENU, txt);
AppendMenu(edit_menu_h, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
InsertMenu( edit_menu_h, 2);
DrawMenuBar();
}
void do_drawing()
{
long temp;
int x,y, gear_1_angle, gear_2_angle;
GrafPtr old_port;
if (pause_state == GO)
{
GetPort(&old_port);
SetPort(draw_window);
MoveTo(last_x,last_y); /* where were we? */
temp = ( point_number * gear_1_speed_num) / gear_1_speed_denom;
gear_1_angle = temp % 360;
gear_2_angle = ((gear_2_speed * point_number) % 360) + gear_1_angle;
gear_2_angle = gear_2_angle % 360;
calculate_point(&x, &y, gear_1_angle, gear_2_angle);
LineTo(x,y);
if( (point_number != 0) && (gear_1_angle == 0) &&
(gear_2_angle == 0) )
stop_drawing();
last_x = x;
last_y = y;
point_number++;
SetPort(old_port);
}
}
stop_drawing()
{
GrafPtr old_port;
GetPort(&old_port);
SetPort(draw_window);
SysBeep(0);
HidePen();
ClosePicture();
pause_state = STOP;
SetPort(old_port);
SetPort(dlg_window);
update_fake_dialog();
}
print_picture( pic, r)
PicHandle pic;
Rect *r;
{
TPPrPort printer_port;
GrafPtr savePort;
TPrStatus prStatus;
THPrint hPrint = NIL;
RgnHandle clip_region;
Rect c_rect;
PrOpen(); /* first print manager call to make (II 154) */
PrintDefault (hPrint = (TPrint **) NewHandle( sizeof( TPrint)));
/* prepare standard print record */
PrDrvrOpen();
PrStlDialog(hPrint); /* style dialog box */
PrJobDialog(hPrint); /* job dialog box */
GetPort(&savePort);
printer_port = PrOpenDoc(hPrint, NIL, NIL);
PrOpenPage(printer_port, NIL);
/* I set up a clip region so that the horizontal and vertical
lines associated with the grow icon don't get printed */
clip_region = NewRgn();
OpenRgn();
c_rect.top = r->top;
c_rect.left = r->left;
c_rect.bottom = r->bottom - 20;
c_rect.right = r->right - 20;
FrameRect(&c_rect);
CloseRgn(clip_region);
printer_port->gPort.clipRgn = clip_region;
DrawPicture(pic, r);
PrClosePage(printer_port);
PrCloseDoc(printer_port);
DisposeRgn(clip_region);
PrPicFile (hPrint, NIL, NIL, NIL, &prStatus);
PrClose();
SetPort(savePort);
}
refresh_window()
{
if (pause_state == STOP) /* only redraw when we are done drawing */
{
SetPort(draw_window);
if (spiro_pic != NIL)
DrawPicture(spiro_pic, &draw_window->portRect);
ShowPen();
}
}
do_command(x)
long x;
{
Str255 name;
long pict_type, dummy;
DialogPtr my_dialog;
sprintf(&pict_type, "PICT");
if (HiWord(x) != 0) /* if we have a real menu event. . . */
switch (HiWord(x))
{
case APPLE_MENU:
if (LoWord(x) == 1)
{
aboutcommand (); /* nifty about box from symantec */
}
else
{
GetItem(apple_menu_h, LoWord(x), name);
OpenDeskAcc(name);
SetPort(draw_window);
}
break;
case FILE_MENU:
switch (LoWord(x))
{
case PRINT_M:
print_picture( spiro_pic, &draw_window->portRect);
break;
case QUIT_M:
quit_whole_program = 1; /* quit flag */
break;
} /* end of case for filemenu */
break; /* break after case file_menu */
case EDIT_MENU:
if (0 == SystemEdit(LoWord(x) - 1) )
switch (LoWord(x))
{
case CUT_M:
case COPY_M:
ZeroScrap();
PutScrap(GetHandleSize(spiro_pic),
pict_type, *spiro_pic);
UnloadScrap();
break;
case PASTE_M:
SysBeep(10);
break;
case CLEAR_M:
ZeroScrap();
UnloadScrap();
break;
}
break; /* break after case edit_menu */
} /* end of switch for hiword */
HiliteMenu(0); /* unhilight menus */
}
calculate_gear_speeds()
{
gear_1_speed_num = (long)gear_2_speed * (long)gear_2_size;
gear_1_speed_denom = (long)(gear_1_size) ;
}
calculate_scale()
{
int size_x, size_y;
/* the goal here is to get a nominator and denominator such that
** the images fit on the screen.
*/
size_x = (draw_window->portRect.right -
draw_window->portRect.left) - 25;
size_y = (draw_window->portRect.bottom -
draw_window->portRect.top) - 25;
if (size_x > size_y)
scale_nom = size_y / 2;
else
scale_nom = size_x / 2;
scale_denom = gear_1_size + gear_2_size + gear_2_size;
}
calculate_midpoint(x,y)
int *x, *y;
{
*x = (draw_window->portRect.left +
draw_window->portRect.right - 16) >> 1;
*y = (draw_window->portRect.top +
draw_window->portRect.bottom - 16) >> 1;
}
draw_gears()
{
PenState pn_state;
Rect box;
int mid_x, mid_y, temp, temp_x, temp_y;
#if 0
GetPenState(&pn_state);
PenMode(patXor);
calculate_midpoint(&mid_x, &mid_y);
temp = (int) (((long)gear_1_size * (long)scale_nom) /
(long)scale_denom);
box.top = mid_y - temp;
box.bottom = mid_y + temp;
box.left = mid_x - temp;
box.right = mid_x + temp;
FrameOval(&box);
temp_x = (int)( ftrig (FTRIG_SINE, gear_1_angle ) *
(gear_1_size + gear_2_size ));
temp_y = (int)( ftrig (FTRIG_COS, gear_1_angle ) *
(gear_1_size + gear_2_size ));
temp_x = (int) (((long)temp_x * (long)scale_nom) /
(long)scale_denom);
temp_y = (int) (((long)temp_y * (long)scale_nom) /
(long)scale_denom);
mid_x += temp_x;
mid_y += temp_y;
temp = (int) (((long)gear_2_size * (long)scale_nom) /
(long)scale_denom);
box.top = mid_y - temp;
box.bottom = mid_y + temp;
box.left = mid_x - temp;
box.right = mid_x + temp;
FrameOval(&box);
SetPenState(&pn_state);
#endif
}
/* CALCULATE POINT
** does the actual spirograph calculation, using global variables
** gear_1_size, gear_2_size.
*/
calculate_point(x,y, gear_1_angle, gear_2_angle)
int *x, *y, gear_1_angle, gear_2_angle;
{
int temp_x, temp_y, mid_x, mid_y;
temp_x = (int)( ftrig (FTRIG_SINE, gear_1_angle ) *
(gear_1_size + gear_2_size ));
temp_y = (int)( ftrig (FTRIG_COS, gear_1_angle ) *
(gear_1_size + gear_2_size ));
temp_x += (int)((ftrig (FTRIG_SINE, gear_2_angle ) *
gear_2_size * pen_position) / PEN_POS_MAX);
temp_y += (int)((ftrig (FTRIG_COS, gear_2_angle ) *
gear_2_size * pen_position) / PEN_POS_MAX);
temp_y = (int) (((long)temp_y * (long)scale_nom) / (long)scale_denom);
temp_x = (int) (( (long)temp_x * (long)scale_nom) / (long)scale_denom);
calculate_midpoint(&mid_x, &mid_y);
*x = temp_x + mid_x;
*y = temp_y + mid_y;
}
control_hit( pt)
Point pt;
{
ControlHandle which_control;
int part_code;
int value;
Rect r;
if (FindControl( pt ,dlg_window, &which_control))
{ /* if we can find a control that was hit */
part_code = TrackControl(which_control, pt, NIL);
if (part_code != 0) /* if they really hit it */
{ /* cant use switch here because controlHandle != int */
if (which_control == pause_control)
{
if (pause_state == GO)
{
stop_drawing();
}
else
{
point_number = 0;
pause_state = GO;
SetPort(draw_window);
EraseRect(&draw_window->portRect);
InvalRect(&draw_window->portRect);
calculate_scale();
calculate_gear_speeds();
draw_gears();
calculate_point(&last_x, &last_y, 0, 0);
point_number++;
if (spiro_pic != NIL)
KillPicture(spiro_pic);
spiro_pic = OpenPicture(&draw_window->portRect);
ShowPen();
}
SetPort(dlg_window);
update_fake_dialog();
}
if (which_control == gear_1_size_control)
handle_scroll_bar(which_control,part_code, &gear_1_size);
if (which_control == gear_2_size_control)
handle_scroll_bar(which_control,part_code, &gear_2_size);
if (which_control == gear_speed_control)
handle_scroll_bar(which_control,part_code, &gear_2_speed);
if (which_control == pen_position_control)
handle_scroll_bar(which_control,part_code, &pen_position);
calculate_gear_speeds();
calculate_scale();
} /* end of if partcode != 0 */
} /* end of FindControl */
}
handle_scroll_bar(which_control, part_code, rtn)
ControlHandle which_control;
int part_code, *rtn;
{
int value;
value = GetCtlValue(which_control);
switch (part_code)
{
case inDownButton:
if (value < GetCtlMax(which_control))
value++;
SetCtlValue(which_control, value);
break;
case inUpButton:
if (value > GetCtlMin(which_control))
value--;
SetCtlValue(which_control, value);
break;
case inPageDown:
if (value < GetCtlMax(which_control) - 5)
value += 5;
SetCtlValue(which_control, value);
break;
case inPageUp:
if (value > GetCtlMin(which_control) + 5)
value -= 5;
SetCtlValue(which_control, value);
break;
case inThumb:
value = GetCtlValue(which_control);
break;
}/* end of switch */
*rtn = value;
}
handle_App4Evt(my_event)
EventRecord *my_event;
{
static WindowPtr former_front_window;
if ( (my_event->message & 0xff000000) == 0x01000000)
{ /* if it is a genuine multifinder app4evt ...*/
if (my_event->message & 0x000001)
{ /* if it is a resume event */
in_foreground = TRUE;
HiliteWindow(former_front_window, TRUE);
UnloadScrap();
}
else
{ /* else it must be a suspend event */
in_foreground = FALSE;
former_front_window = FrontWindow();
HiliteWindow(former_front_window, FALSE);
LoadScrap();
}
}
}
my_get_event(my_event)
EventRecord *my_event;
{
long wait_time;
if (!wne_implemented)
{
SystemTask(); /* make sure someone else get a hunk of time */
return(GetNextEvent(everyEvent, my_event));
}
else /* else wait_next_event is here */
{
wait_time = 1L; /* the default is to give away a small piece of
time */
if ( (in_foreground) && (pause_state == STOP))
wait_time = 20L; /* give up a larger amount of time */
if (!in_foreground)
wait_time = 10000L; /* give up lots of time in background */
return(WaitNextEvent(everyEvent, my_event, wait_time, NIL));
/* NIL refers to a cursor region */
}
}
check_events()
{
EventRecord my_event;
Point location;
WindowPtr which_window;
ControlHandle which_control;
Rect bounds;
char the_key;
GrafPtr old_port;
long new_size;
if (!my_get_event(&my_event)) /* uses wait_next_event if it can */
return(0); /* if no event, go draw something maybe */
if (my_event.what == mouseDown)
switch(FindWindow(my_event.where, &which_window) )
{
case inDesk:
SysBeep(1);
break;
case inSysWindow: /* someone else's window */
SystemClick(&my_event, which_window); /* pass it on */
break;
case inGoAway: /* if they want to go away */
if (TrackGoAway(which_window, my_event.where))
quit_whole_program = 1;
break;
case inMenuBar:
do_command(MenuSelect(my_event.where) );
break;
case inContent: /* if in content of current window */
if (which_window == dlg_window)
{
GetPort(&old_port);
if (old_port != dlg_window)
SelectWindow(which_window);
location = my_event.where;
SetPort(dlg_window); /* make dialog the current port */
GlobalToLocal(&location);
control_hit(location);
}
if (which_window == draw_window)
{
SelectWindow(which_window);
}
break;
case inDrag:
if( (which_window == draw_window) ||
(which_window == dlg_window))
{
SetRect( &bounds, 15, 25, screenBits.bounds.right - 15,
screenBits.bounds.bottom - 15);
DragWindow(which_window, my_event.where, &bounds);
}
break;
case inGrow:
if (which_window == draw_window)
{
bounds.top = 50; /* min vert size */
bounds.bottom = screenBits.bounds.bottom;/* max vert*/
bounds.left = 50; /* min horiz size */
bounds.right = screenBits.bounds.right; /* max horiz*/
new_size = GrowWindow(draw_window,
my_event.where, &bounds);
SizeWindow(draw_window,
LoWord(new_size), HiWord(new_size), TRUE);
EraseRect(&draw_window->portRect); /*clear whole wind */
InvalRect(&draw_window->portRect); /* update */
calculate_scale();
}
default:
/* SysBeep(1); */ /* why do we get here? */
break;
} /* end switch FindWindow */
else if (my_event.what == updateEvt)
{
if ((WindowPtr)my_event.message == draw_window)
{
BeginUpdate(draw_window);
refresh_window();
EndUpdate(draw_window);
DrawGrowIcon(draw_window);
}
if ((WindowPtr)my_event.message == dlg_window)
{
GetPort(&old_port);
SetPort(dlg_window);
BeginUpdate(dlg_window);
update_fake_dialog();
EndUpdate( dlg_window);
SetPort(old_port);
/* ??????????????????????????? */
BeginUpdate(dlg_window); /* to clear out update rgn */
EndUpdate(dlg_window);
}
}
else
if (my_event.what == activateEvt)
{
if (((WindowPtr)my_event.message == draw_window) &&
(my_event.modifiers & activeFlag))
{
SetPort(draw_window); /* make my window the current port */
DrawGrowIcon(draw_window);
}
if (((WindowPtr)my_event.message == draw_window) &&
!(my_event.modifiers & activeFlag))
{
DrawGrowIcon(draw_window); /* which clears out the icon */
}
if (((WindowPtr)my_event.message == dlg_window) &&
(my_event.modifiers & activeFlag) )
{
SetPort(dlg_window); /* make my window the current port */
}
}
else if (my_event.what == keyDown)
{
if( my_event.modifiers & cmdKey)
{
the_key = my_event.message & charCodeMask;
do_command(MenuKey(the_key));
}
}
else if (my_event.what == app4Evt)
{
handle_App4Evt(&my_event);
}
}
void main()
{
init_managers();
setup_menus();
ftrig(FTRIG_INIT, 0);
setup_fake_dialog();
setup_draw_window();
wne_implemented = WNE_avail(); /* look for wait next event */
while (!quit_whole_program)
{
/* The 2-function loop here is designed so that events can be
** monitored even when drawing is in progress. Other
** programs need time on a regular basis, and can get it during
** check_events(). This does not exactly speed up the drawing
** process. My goal is to allow a lengthy process to proceed,
** and still give up time to other programs. Comments on
** this method of doing things are invited. (I haven't seen
** anyone else doing it this way, so I'm not sure it is best)
*/
do_drawing(); /* this part does the actual drawing */
check_events(); /* this checks for events, and processes them */
}
ftrig(FTRIG_END, 0);
remember_window_positions();
}